/*
 * Copyright (c) 2012 Andrea De Rinaldis <ozioso at ipignoli.dyndns.org>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.dyndns.ipignoli.droidcruise.timer;

import java.util.LinkedList;
import java.util.List;
import org.dyndns.ipignoli.droidcruise.DroidCruise;
import org.dyndns.ipignoli.droidcruise.position.CurrentPosition;
import org.dyndns.ipignoli.droidcruise.position.MyCoordinate;
import org.dyndns.ipignoli.droidcruise.position.MyLocation;
import org.dyndns.ipignoli.droidcruise.ui.GpsUI;
import org.dyndns.ipignoli.droidcruise.ui.RadarUI;
import org.dyndns.ipignoli.droidcruise.ui.RouteUI;
import org.dyndns.ipignoli.droidcruise.ui.SpeedUI;
import android.os.Handler;
import android.util.Log;


public class UIUpdater extends Handler implements Runnable{
  private static final int WAIT              = 2;
  private static final int WAYPOINT_ACCURACY = 1;

  private static UIUpdater instance;

  public static UIUpdater getInstance(SpeedUI speedUI,
      CurrentPosition currentPosition, GpsUI gpsUI, RouteUI routeUI,
      RadarUI radarUI){
    if(instance == null)
      instance = new UIUpdater();
    instance.init(speedUI, currentPosition, gpsUI, routeUI, radarUI);
    return instance;
  }

  public static UIUpdater getInstance(){
    return instance;
  }

  private SpeedUI            speedUI;
  private CurrentPosition    currentPosition;
  private GpsUI              gpsUI;
  private RouteUI            routeUI;
  private RadarUI            radarUI;
  private boolean            running;
  private List<MyCoordinate> locationBuffer;

  private UIUpdater(){
    this.speedUI = null;
    this.currentPosition = null;
    this.gpsUI = null;
    this.routeUI = null;
    this.radarUI = null;
    running = false;
    locationBuffer = null;
  }

  private void init(SpeedUI speedUI, CurrentPosition currentPosition,
      GpsUI gpsUI, RouteUI routeUI, RadarUI radarUI){
    this.speedUI = speedUI;
    this.currentPosition = currentPosition;
    this.gpsUI = gpsUI;
    this.routeUI = routeUI;
    this.radarUI = radarUI;
    Log.println(DroidCruise.DEBUG_LEVEL, "droidcruise.UIUpdater.init",
        "UIUpdater initialized");
  }

  public synchronized void stop(){
    Log.println(DroidCruise.DEBUG_LEVEL, "droidcruise.UIUpdater.stop",
        "stopping UIUpdater");
    while(isRunning())
      try{
        Thread.sleep(100);
      }catch(InterruptedException e){
        return;
      }
    Log.println(DroidCruise.DEBUG_LEVEL, "droidcruise.UIUpdater.stop",
        "old UIUpdater instance stopped");
    removeCallbacks(this);
    Log.println(DroidCruise.DEBUG_LEVEL, "droidcruise.UIUpdater.stop",
        "UIUpdater stop completed");
  }

  public boolean isRecordingRoute(){
    return getLocationBuffer() != null;
  }

  public boolean startRouteRecording(){
    if(currentPosition == null || currentPosition.getLastPosition() == null)
      return false;
    Log.println(DroidCruise.DEBUG_LEVEL,
        "droidcruise.UIUpdater.setLocationBuffer", "route recording started");
    setLocationBuffer(new LinkedList<MyCoordinate>());
    return true;
  }

  public List<MyCoordinate> stopRouteRecording(){
    List<MyCoordinate> ret = getLocationBuffer();
    Log.println(DroidCruise.DEBUG_LEVEL,
        "droidcruise.UIUpdater.setLocationBuffer",
        "route recording stopped - route=" + ret);
    setLocationBuffer(null);
    return ret;
  }

  private synchronized List<MyCoordinate> getLocationBuffer(){
    return locationBuffer;
  }

  private synchronized void setLocationBuffer(List<MyCoordinate> locationBuffer){
    this.locationBuffer = locationBuffer;
  }

  public synchronized void restart(){
    Log.println(DroidCruise.DEBUG_LEVEL, "droidcruise.UIUpdater.restart",
        "restarting UIUpdater");
    stop();
    postDelayed(this, 0);
    Log.println(DroidCruise.DEBUG_LEVEL, "droidcruise.UIUpdater.restart",
        "UIUpdater restart completed");
  }

  @Override
  public void run(){
    long t = System.currentTimeMillis();
    Log.println(DroidCruise.DEBUG_LEVEL, "droidcruise.UIUpdater.run",
        "UIUpdater run loop started at " + t);
    setRunning(true);
    currentPosition.updateLastPosition();

    List<MyCoordinate> locationsBuffer = getLocationBuffer();
    MyLocation lastLocation = currentPosition.getLastPosition();
    if(locationsBuffer != null
        && lastLocation != null
        && (locationBuffer.size() == 0
            || locationBuffer.get(locationBuffer.size() - 1).getLatitude() != lastLocation
                .getLatitude() || locationBuffer.get(locationBuffer.size() - 1)
            .getLongitude() != lastLocation.getLongitude())){
      locationBuffer.add(lastLocation);
      if(locationBuffer.size() > 2){
        MyLocation previous =
            (MyLocation)locationBuffer.get(locationBuffer.size() - 3);
        MyLocation middle =
            (MyLocation)locationBuffer.get(locationBuffer.size() - 2);
        if(Math.abs(previous.distanceTo(lastLocation)
            - previous.distanceTo(middle) - lastLocation.distanceTo(middle)) <= WAYPOINT_ACCURACY)
          locationBuffer.remove(locationBuffer.size() - 2);
      }
    }

    speedUI.setSpeed(currentPosition.getCurrentSpeed(), currentPosition
        .getAverageSpeed(), currentPosition.getLastPosition() == null ? null
        : currentPosition.getLastPosition().getRoute(), radarUI.isRadarSet(),
        gpsUI.isAvailable());

    speedUI.sendNotification();
    gpsUI.sendNotification();
    routeUI.sendNotification();
    radarUI.sendNotification();

    Log.println(DroidCruise.DEBUG_LEVEL, "droidcruise.UIUpdater.run",
        "UIUpdater run loop completed in " + (System.currentTimeMillis() - t)
            + " msec");
    postDelayed(this, Math.max(0, WAIT * 1000 - System.currentTimeMillis() + t));
    setRunning(false);
  }

  private synchronized boolean isRunning(){
    return running;
  }

  private synchronized void setRunning(boolean running){
    this.running = running;
  }
}
